﻿using GN.Pay.Application.Dto.Input;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using GN.Pay.Utils;
using Abp.Dependency;

namespace GN.Pay.Application.GatewayApp
{
    /// <summary>
    /// 网关服务信息
    /// </summary>
    public class GatewayServiceInfo
    {
        private Func<object, IServiceInput, object> resultCall = null;
        private Action<object, IServiceInput> notResultCall = null;
        private readonly IIocManager iocManager;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="iocManager"></param>
        /// <param name="methodInfo"></param>
        public GatewayServiceInfo(IIocManager iocManager, MethodInfo methodInfo)
        {
            if (iocManager == null)
            {
                throw new ArgumentNullException(nameof(iocManager));
            }
            if (methodInfo == null)
            {
                throw new ArgumentNullException(nameof(methodInfo));
            }
            this.iocManager = iocManager;
            this.ServiceType = methodInfo.ReflectedType ?? methodInfo.DeclaringType;
            var attr = methodInfo.GetCustomAttribute<GatewayServiceAttribute>();
            string funName = "网关服务函数(" + ServiceType.FullName + "." + methodInfo.Name + ")";
            if (attr == null)
            {
                throw new ArgumentException(funName + "未配置服务特性(GatewayServiceAttribute)。");
            }
            this.ServiceName = attr.SerivceName;
            this.MethodInfo = methodInfo;
            var pars = MethodInfo.GetParameters();
            if (pars.Length != 1)
            {
                throw new ArgumentException(funName + "参数数量不正确。");
            }
            var p = pars[0];
            var limitInputType = typeof(IServiceInput);
            if (!p.ParameterType.IsClass)
            {
                throw new ArgumentException(funName + "的输入参数必须是类型。");
            }
            if (p.ParameterType.GetConstructor(Type.EmptyTypes) == null)
            {
                throw new ArgumentException(funName + "的输入参数必须支持无构造参数。");
            }
            if (!limitInputType.IsAssignableFrom(p.ParameterType))
            {
                throw new ArgumentException(funName + "的参数类型未实现 " + limitInputType.FullName + " 接口");
            }            
            this.InputType = p.ParameterType;
            CreateCall();
        }

        /// <summary>
        /// 
        /// </summary>
        private void CreateCall()
        {
            var instanceParam = Expression.Parameter(typeof(object));
            var inputParam = Expression.Parameter(typeof(IServiceInput), "input");

            var serviceInstanceParam = Expression.Convert(instanceParam, this.ServiceType);
            var inputInstanceParam = Expression.Convert(inputParam, this.InputType);

            var methodExpression = Expression.Call(serviceInstanceParam, this.MethodInfo, inputInstanceParam);
            if (this.MethodInfo.ReturnType.Equals(typeof(void)))
            {
                var resultExpression = Expression.Lambda<Action<object, IServiceInput>>(methodExpression, instanceParam, inputParam);
                this.notResultCall = resultExpression.Compile();
            }
            else
            {
                if (MethodInfo.IsAsyncMethod())
                {
                    if (MethodInfo.ReturnType == typeof(Task))
                    {

                    }
                    else
                    {

                    }
                }
                else
                {
                    var resultExpression = Expression.Lambda<Func<object, IServiceInput, object>>(methodExpression, instanceParam, inputParam);
                    this.resultCall = resultExpression.Compile();
                }
            }
        }

        public static async Task AwaitTaskWithPostActionAndFinally(Task actualReturnValue, Func<Task> postAction, Action<Exception> finalAction)
        {
            Exception exception = null;

            try
            {
                await actualReturnValue;
                await postAction();
            }
            catch (Exception ex)
            {
                exception = ex;
                throw;
            }
            finally
            {
                finalAction(exception);
            }
        }

        /// <summary>
        /// 获取服务名称
        /// </summary>
        public string ServiceName { get; private set; }

        /// <summary>
        /// 获取服务类型
        /// </summary>
        public Type ServiceType { get; private set; }

        /// <summary>
        /// 输入类型
        /// </summary>
        public Type InputType { get; private set; }

        /// <summary>
        /// 方法信息
        /// </summary>
        public MethodInfo MethodInfo { get; private set; }

        /// <summary>
        /// 调用
        /// </summary>
        /// <param name="input">输入</param>
        /// <returns></returns>
        public object Call(IServiceInput input)
        {
            var instance = iocManager.Resolve(this.ServiceType);
            if (resultCall != null)
            {
                return resultCall(instance, input);
            }
            notResultCall(instance, input);
            return null;
        }
    }
}
